/* 
 * MyValidator - validates input fields of any form
 * Features: - загрузка необходимых валидаторов;
 *           - написание собственных валидаторов;
 *           - загрузка необходимых функций, которые отвечают за отображение ошибок;
 *           - написание собственных функций отображения ошибок валидации;
 *           - валидация вешается на тот элемент формы, значения которого нужно
 *             проверить;
 *           - возможность добавлять элемент "для прослушивания" (bindedElement - любой элемент DOM) и
 *             события для этого элемента (triggeredEvents), когда необходимо выполнить валидацию.
 *             Если элемент "для прослушивания" не задан, будет использоваться
 *             элемент и события, заданные в глобальных настройках globalOptions.
 *             Если элемент не задан и в глобальных настройках - используется текущий
 *             элемент, для которого выполняется валидация.
 *           - возможность задавать несколько валидаторов.
 *           - возможность задавать свои сообщения об ошибках или использовать
 *             сообщения по умолчанию для каждого валидатора.
 *           - возможность задавать текущую локаль (язык текста ошибок будет соответствовать локали)
 *
 * Options:  Object
 *           - bindedElement : $(selector)
 *             Элемент "для прослушивания".
 *           - triggeredEvents : 'event1, event2, ..., eventN',
 *             События для прослушивания.
 *           - processors : [{method : 'The validation method1 name',
 *                            validationValue: 'The validation value',
 *                            messages : {'en' : 'Error message if validation is not correct',
 *                                        'ru' : 'Ошибка ошибка ошибка'}},
 *                           {method : 'The validation method2 name'}
 *                           ]
 *           Массив валидаторов и опций валидации.
 *           Если какая-то из опций не указана, будут использоваться настройки по умолчанию.
 *           Список доступных валидаторов (The validation method name) можно найти в папке validators:
 *                            - "notEmpty" (Проверяет не пустое ли поле);
 *                            - "minLength" (Минимальное кол-во символов в поле);
 *                            - "regExp" (Регулярка, которой должен соответствавать текст в поле)
 *           - errorFunction : 'error function',
 *             (Функция отображения ошибок валидации. Список доступных можно найти  в папке errorviews)
 *           - show (true | false)
 *             Выполнять или нет валидацию.
 *
 * Global options: 
 *           Любые из перечисленных настроек могут быть установленны глобально 
 *           для плагина. Для установки глобальных параметров используйте
 *           следующий пример в секции $(document).ready вашего файла.
 *           //Пример массива глобальных параметров
 *           var globalOptions = {bindedElement : $("#forma"),
 *                                triggeredEvents: "submit",
 *                                processors : [{method:'notEmpty'}]},
 *                                errorFunction : "list";
 *
 *
 * !!! Инициализация плагина !!! ОБЯЗАТЕЛЬНО!!!
 *           $.myValidateInit(globalOptions, validatorStack,
                              errorMessageShowFunctionStack, "ru");
 *
 *           - Второй параметр в функции $.myValidateInit - validatorStack
 *             Массив валидаторов, использующихся плагином (в папке validators)
 *             Например,  validatorStack = ['notEmpty', 'minLength', 'maxLength', 'regExp'];
 *
 *           - Третий параметр - errorMessageShowFunctionStack
 *             Массив функций, использующихся для отображения ошибок валидации (в папке errorviews)
 *             Например,  errorMessageShowFunctionStack = ['list', 'popup'];
 *
 *           - Четвертый параметр - локаль. Т.е. язык отображения ошибок валидации. 
 *
 *
 */

(function($) {
    //Default options.
    //They are could be rewritten by globalOptions and then
    //by individual options for each element using validator
    var defaults = {bindedElement   : null,
                    triggeredEvents : "keyup, change, click",
                    processors      : [{method : 'notEmpty'}],
                    errorFunction   : "list",
                    show            : true};

    //Global options used by any element that is validated
    var globalOptions = {};

    //Stack of validators are used by plugin
    //They must be setted by $.myValidateInit
    $.validatorsStack = {};

    //Stack of functions that show errors of validation
    //They must be setted by $.myValidateInit
    $.errorShowFunctionsStack = {};

    //Variable that saves client locale
    var locale = "en";

    $.myValidateInit = function(optionsGlob, validators, errorShowFunc, clientLocale) {
        //Init globalOptions for plugin
        globalOptions = $.extend(globalOptions, optionsGlob);

        //Initialize $.validatorsStack
        var countVal = validators.length;
        for (var i=0; i<countVal; i++) {
            $.ajax({async:false,
                    url: '/scripts1/validators/' + validators[i] + ".js",
                    dataType: 'text',
                    success: function (validationFunction, textStatus) {
                        $.validatorsStack[validators[i]] = validationFunction;
                    }
            });
        };

        //Initialize $.errorShowFunctionsStack
        var countErrF = errorShowFunc.length;
        for (var j=0; j<countErrF; j++) {
            $.ajax({async:false,
                    url: '/scripts1/errorviews/' + errorShowFunc[j] + ".js",
                    dataType: 'text',
                    success: function (errorFunc, textStatus) {
                        $.errorShowFunctionsStack[errorShowFunc[j]] = errorFunc;
                    }
            });
        };

        if (typeof(clientLocale)!="undefined") {
            locale = clientLocale.substr(0, 2);
        };
    };


    $.fn.myvalidate = function(params) {
        //This element
        var element = this;

        //Error message id in DOM
        var idError = "error" + element.attr("name");
        
        //Options for this element validations
        var options = $.extend({}, defaults, globalOptions, params);
        
        //Events
        var trigerredEvents = options.triggeredEvents.replace(/\,/g, "");

        //Function that shows errors of validation for this element
        var showErrorFunction = "";

        //The number of the validators are needed to check element value
        var countProcessors = options.processors.length;

        //Initialization functions
        function initElementEvent(options) { 
            //Init binded element end event
            if (options.bindedElement == null) {
                options.bindedElement = element;
            };
        };

        function initErrorMessages(options) {    
            //Init error messages
            for (var i=0; i<countProcessors; i++) {
                var messagesCur = options.processors[i].messages;
                var validationMethod = options.processors[i].method;
                if (typeof($.validatorsStack[validationMethod]) != "undefined") {
                    options.processors[i].message = eval($.validatorsStack[validationMethod] +
                                                         "getErrorMessage();");
                } else {
                    console.log("ERROR! Validator " + validationMethod + " has not be setted!");
                };
            };
        };

        function initErrorFunction(options) {
            //Init showErrorFunction
            if (typeof($.errorShowFunctionsStack[options.errorFunction]) != "undefined") {
                showErrorFunction = $.errorShowFunctionsStack[options.errorFunction];
            };
        };
        //End initialization functions

        //Main validation function
        function validate(options) {
            //validate function
            //validate here
            if (options.show && countProcessors) {
                $(options.bindedElement).bind(trigerredEvents.toString(), function(e) {
                    //Execute main validation function
                    $("#"+idError).remove();
                    //Full stack of messages that will displayed
                    var messageText = [];
                    var j = 0;

                    for(var i=0; i<countProcessors; i++) {
                        var elemValue = element.attr("value");
                        var neededValue = options.processors[i].validationValue;
                        var isError = eval($.validatorsStack[options.processors[i].method] +
                                           options.processors[i].method + "();");
                        if (isError) {
                            var readyText = options.processors[i].message.replace(/%value%/g, element.attr("value"));
                            if (typeof(neededValue) != "undefined") {
                                readyText = readyText.replace(/%neededValue%/g, neededValue.toString());
                            };
                            messageText[j++] = readyText;
                        };
                    };
                    
                    if (messageText.length) {
                        //Triger show error function
                        eval(showErrorFunction + options.errorFunction + "();");
                        e.stopPropagation();
                        return false;
                    };
                });
            };
        };


        //Init state
        initElementEvent(options);
        initErrorMessages(options);
        initErrorFunction(options);

        //Execute validation
        validate(options);
        
        return this;
    };

})(jQuery);
